Java 之 JUC
1. JUC 简介
在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中的 Collection 实现等;
2. volatile 关键字
- volatile 关键字: 当多个线程进行操作共享数据时,可以保证内存中的数据是可见的;相较于 synchronized 是一种
较为轻量级的同步策略; - volatile 不具备”互斥性”;
- volatile 不能保证变量的”原子性”;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40// 使用 volatile 之前
public class TestVolatile{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while(true){
if(td.isFlag()){
System.out.println("########");
break;
}
}
}
}
class ThreadDemo implements Runnable{
private boolean flag = false;
public void run(){
try{
// 该线程 sleep(200), 导致了程序无法执行成功
Thread.sleep(200);
}catch(InterruptedException e){
e.printStackTrace();
}
flag = true;
Sytem.out.println("flag="+isFlag());
}
public boolean isFlag(){
return flag;
}
public void setFlag(boolean flag){
this.flag = flag;
}
}
1 | // 解决问题方式一: 同步锁 |
3. i++ 的原子性问题
i++的操作实际上分为三个步骤: “读-改-写”;
- 原子性: 就是”i++”的”读-改-写”是不可分割的三个步骤;
- 原子变量: JDK1.5 以后, java.util.concurrent.atomic包下,提供了常用的原子变量;
- 原子变量中的值,使用 volatile 修饰,保证了内存可见性;
CAS(Compare-And-Swap) 算法保证数据的原子性;
1 | int i = 10; |
执行步骤:
int temp = i;
i = i + 1;
i = temp;
1 | // 测试类 |
3.1 CAS 算法
CAS(Compare-And-Swap) 算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于
管理对共享数据的并发访问;
- CAS 是一种无锁的非阻塞算法的实现;
- CAS 包含了三个操作数:
- 需要读写的内存值: V
- 进行比较的预估值: A
- 拟写入的更新值: B
当且仅当 V == A 时, V = B, 否则,将不做任何操作;
1 | // 模拟CAS 算法 |
4. 并发容器类
Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能;
4.1 ConcurrentHashMap
ConcurrentHashMap 同步容器类是 Java5 增加的一个线程安全的哈希表;介于 HashMap 与 Hashtable 之间;
内部采用”锁分段”机制替代Hashtable的独占锁,进而提高性能;
此包还提供了设计用于多线程上下文中的Collection实现: ConcurrentHashMap,ConcurrentSkipListMap
ConcurrentSkipListSet, CopyOnWriteArrayList 和 CopyOnWriteArraySet;
当期望许多线程访问一个给定collection时,ConcurrentHashMap通常优于同步的HashMap;
ConcurrentSkipListMap通常优于同步的TreeMap;
当期望的读数和遍历远远大于列表的更新数时, CopyOnWriteArrayList优于同步的ArrayList;
4.2 CountDownLatch(闭锁)
CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51// 测试类: 计算多线程的执行时间
public class TestCountDownLatch{
public static void main(String[] args){
final CountDownLatch latch = new CountDownLatch(10);
LatchDemo ld = new LatchDemo(latch);
long start = System.currentTimeMillis();
// 创建10个线程
for(int i=0; i<10; i++){
new Thread(ld).start();
}
try{
latch.await();
}catch(InterruptedException e){
}
long end = System.currentTimeMillis();
System.out.println("耗费时间为:"+(end - start));
}
}
class LatchDemo implements Runnable{
private CountDownLatch latch;
// 有参构造器
public LatchDemo(CountDownLatch latch){
this.latch = latch;
}
public void run(){
synchronized(this){
try{
// 打印50000以内的偶数
for(int i=0; i<50000; i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}finally{
// 线程数量递减
latch.countDown();
}
}
}
}
5. 创建执行线程的方式三
相较于实现 Runnable 接口的方式,实现 Callable 接口类中的方法可以有返回值,并且可以抛出异常;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38// 测试类
public class TestCallable{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
// 执行 Callable 方式,需要 FutureTask 实现类的支持
// FutureTask 实现类用于接收运算结果, FutureTask 是 Future 接口的实现类
FutureTask<Integer> result = new FutureTask<>(td);
new Thread(result).start();
// 接收线程运算后的结果
try{
// 只有当 Thread 线程执行完成后,才会打印结果;
// 因此, FutureTask 也可用于闭锁
Integer sum = result.get();
System.out.println(sum);
}catch(InterruptedException | ExecutionException e){
e.printStackTrace();
}
}
}
class ThreadDemo implements Callable<Integer>{
// 需要实现的方法
public Integer call() throws Exception{
// 计算 0~100 的和
int sum = 0;
for(int i=0; i<=100; i++){
sum += i;
}
return sum;
}
}
6. 同步锁(Lock)
参考 “java 多线程间通信”
1 | // 测试类: 以卖票为例 |
1 | // 练习: 程序按序交替 |
7. ReadWriteLock(读写锁)
1 | // 测试类 |
8. 线程八锁
1 | // 测试类 |
9. 线程池
线程池提供了一个线程队列,队列中保存着所有等待状态的线程;
避免了创建与销毁线程的额外开销,提高了响应速度;
- 线程池的体系结构
- java.util.concurrent.Executor: 负责线程的使用和调度的根接口;
- ExecutorService: 子接口,线程池的主要接口;
- ThreadPoolExecutor: 线程池的实现类;
- ScheduledExecutorService: 子接口,负责线程的调度;
- ScheduledThreadPoolExecutor: 继承了线程池的实现类,实现了负责线程调度的子接口;
- 工具类: Executors
- ExecutorService newFixedThreadPool(): 创建固定大小的线程池;
- ExecutorService newCachedThreadPool(): 缓存线程池,线程池中线程的数量不固定,可以根据需求自动更改数量;
- ExecutorService newSingleThreadExecutor(): 创建单个线程池, 线程池中只有一个线程;
- ScheduledExecutorService newScheduledThreadPool(): 创建固定大小的线程,可以延时或定时的执行任务;
1 | public class TestThreadPool{ |
9.1 线程调度
1 | public class TestScheduledThreadPool{ |
10 Fork/Join 框架
1 | public class TestForkJoinPool{ |